//
// File  : BookingController.cpp
// Team  : Dangling Pointers
// Class : CST236 - Software Testing
//
// BookingController implementation.
//

#include "BookingController.h"
#include "CustomerDatabase.h"
#include "Global.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cctype>


// constructor
BookingController::BookingController(FlightDatabase* flightdatabase, CustomerDatabase* customerdatabase) 
	: m_flightdatabase(flightdatabase), m_customerdatabase(customerdatabase)
{
	displayMenu();
}

//
// displayMenu : void
//
// method will display the menu for the BookingController and then prompt for user
// input corresponding to menu choice.
//
void BookingController::displayMenu()
{
	while (true) 
	{
		clearConsole();

		std::string menuName = "Booking Menu";
		std::cout << std::endl << "====== " << menuName << " ======" << std::endl;
		std::cout << "1. Book Flight." << std::endl;
		std::cout << "2. Cancel Flight." << std::endl;
		std::cout << "3. Print Boarding Pass." << std::endl;
		std::cout << "4. Main Menu." << std::endl;

		int input = getUserInput(4);

		switch(input)
		{
		case 1:
			clearConsole();
			bookFlight();
			pressEnterToContinue();
			break;

		case 2:
			clearConsole();
			cancelFlight();
			pressEnterToContinue();
			break;

		case 3:
			clearConsole();
			printBoardingPass();
			pressEnterToContinue();
			break;

		default:
			return;
		}
	}
}

//
// bookFlight : void
//
// method will book a customer into a flight's first-class or coach seat
//
void BookingController::bookFlight()
{
	std::cout << std::endl << "====== Book Flight ======" << std::endl;

	// get flight number
	std::string flightNumber;

	while (true) 
	{
		getFlightFromUser(flightNumber);

		// Enter nothing to break from this menu
		if (flightNumber.size() == 0)
			return;

		bool finished;

		try 
		{
			m_flightdatabase->getFlight(flightNumber);
			finished = false;
		} 
		catch (std::exception) 
		{
			std::cout << "Flight not found, please re-enter (or enter nothing to cancel)" << std::endl;
			continue;
		}

		// no exception thrown, finished changed to true, flight object is grabbed
		if (finished == false)
			finished = true;
		
		// validate the flight number entered by the user
		if ( validateFlightNumber(flightNumber) ) 
		{
			//
			// check to see if all seats are full in the current flight. If so, then prompt to enter
			// another flight.
			//
			if( m_flightdatabase->getFlight(flightNumber).getCoachClassCount() > Flight::MAX_SEATS &&
			    m_flightdatabase->getFlight(flightNumber).getFirstClassCount() > Flight::MAX_SEATS )
			{
				std::cout << "All seats are currently booked for flight " << flightNumber << "."
			        << " Please book another flight." << std::endl;
				continue;
			}
			
			break;
		}
		
		std::cout << "Invalid entry! ";
	}

	// grab relevant flight object
	const Flight& flight = m_flightdatabase->getFlight(flightNumber);

	// get departure and arrival times
	std::string departure = flight.getDepartureTime();
	std::string arrival = flight.getArrivalTime();

	// System displays flight #, time and place it departs, and time and place it arrives

	std::cout << std::endl << "======" 
		<< " Flight Booking Display ======" 
		<< std::endl;

	std::cout << std::left
		<< std::setw(20) << "Flight#" 
		<< std::setw(20) << "Departure" 
		<< std::setw(20) << "Arrival" 
		<< "Status" 
		<< std::endl;

	flight.print();
	std::cout << std::endl;

	//
	// Prompt's User name, returns null if name not in database, otherwise, returns name in string
	// If username is blank, exit this method.
	//
	std::string userName = getUserName();

	if (userName == "")
		return;

	clearConsole();

	//Prompt User "Choose 1)first class 2) second class
	flight.printFirstClassSeats();
	flight.printCoachClassSeats();

	flight.print();

	while (true) 
	{
		std::string menuName = "Choose Class ";
		std::cout << std::endl << "====== " << menuName << " ======" << std::endl;
		std::cout << "1. First Class." << std::endl;
		std::cout << "2. Coach Class." << std::endl;
		std::cout << "3. Exit." << std::endl;

		int input = getUserInput(3);

		if (input == 1)
		{
			//
			// Check if the flight is full before adding a flight
			//
			if( Flight::MAX_SEATS < m_flightdatabase->getFlight(flightNumber).getFirstClassCount() )
			{
				std::cout << "\nAll first class seats for flight " << flightNumber
					<< " are full!" << std::endl;

				continue;
			}

			m_flightdatabase->addFirstClassSeating(flightNumber, userName);
			m_customerdatabase->updateCustomerFlight(userName, flightNumber);
			break;
		}
		if (input == 2)
		{
			//
			// Check if the flight is full before adding a flight
			//
			if( Flight::MAX_SEATS < m_flightdatabase->getFlight(flightNumber).getCoachClassCount() )
			{
				std::cout << "\nAll coach seats for flight " << flightNumber
					<< " are full!" << std::endl;

				continue;
			}

			//
			// Check if the flight is full before adding a flight
			//
			m_flightdatabase->addCoachClassSeating(flightNumber, userName);
			m_customerdatabase->updateCustomerFlight(userName, flightNumber);
			break;
		}

		if (input == 3)
			break;
	}

	clearConsole();

	// print first and coach class seats for the given flight number
	m_flightdatabase->getFlight(flightNumber).printFirstClassSeats();
	m_flightdatabase->getFlight(flightNumber).printCoachClassSeats();
}

//
// cancelFlight : void
//
// method will cancel a flight booking
//
void BookingController::cancelFlight()
{
	std::cout << "======Cancel Flight======" << std::endl;

	std::string cancel;
	std::string flightNumber;
	std::string customerName = promptUserName();

	if (customerName == "")
		return;

	while (true)
	{
		getFlightFromUser(flightNumber);

		if ( !validateFlightNumber(flightNumber) )
		{
			std::cout << "Invalid Flight Number!" << std::endl;
			continue;
		}

		m_flightdatabase->getFlight(flightNumber).print();

		std::cout << "Confirm Cancellation? (y/n): ";
		std::cin  >> cancel;

		if(cancel == "y")
		{
			m_customerdatabase->updateCustomerFlight(customerName, "");
			m_flightdatabase->removePassengerFromSeatingChart(flightNumber, customerName);

			std::cout << "Your flight has been cancelled!" << std::endl << "\n";
			return;
		}
		else
		{
			std::cout << "Your flight has not been cancelled!" << std::endl << "\n";
			return;
		}
	}
}

//
// printBoardingPass : void
//
// method will print out boarding pass information to console
//
void BookingController::printBoardingPass()
{
	std::cout << "======Boarding Pass======" << std::endl;
	std::string flight;
	std::string userName = getUserName();
	std::cout << std::endl;

	flight = m_customerdatabase->getCustomer(userName).getFlight();

	if(flight == "")
	{
		std::cout << "No flight reservation for " << userName << ".\n" << std::endl;
		return;
	}
	else
	{
		std::cout << "======Flight======" << std::endl;
		m_flightdatabase->getFlight(flight).print();
	}
}

std::string BookingController::promptUserName()
{
	bool customerFound = true;
	std::string name;

	while (true) 
	{
		std::cout << "Enter your name: ";
		getline(std::cin, name);

		if (name.size() == 0) 
			return "";

		try 
		{
			m_customerdatabase->getCustomer(name);
			customerFound = true;
		} 
		catch (std::exception) 
		{
			customerFound = false;
		}

		if (customerFound) 
			break;

		std::cout << "Invalid Name! ";
	}

	return name;
}

//
// getUserName : std::string
//
// Prompts user for name, validates name, and checks customer records to see if Customer 
// object is present, if so, returns name in a string
//
std::string BookingController::getUserName()
{
	// Prompt user for name
	std::string userName = promptUserName();

	// if the user name is an empty string, just return it to the client
	if (userName == "") 
		return userName;

	// Check customerDatabasePointer to confirm name in records
	return m_customerdatabase->getCustomer(userName).getName();
}

//
// validateName : bool
//
// validates name based on requirements, returns 1 if name is validated, returns 0 if name does not meet requirements
//
bool BookingController::validateName(std::string name)
{
	// Ensure name is between 1 and 20 characters
	if (name.length() >= 20 || name.length() == 0)
		return false;

	// Ensure name is alphabetic
	for (size_t i = 0; i <= name.length(); ++i)
	{
		if ( !isalpha(name[i]) )
			return false;
	}

	return true;
}